import numpy as np
import wx
import threading
from pubsub import pub
import time
import matplotlib.pyplot as plt
from passengerClass import Passenger
from elevatorClass import Elevator
import globalConfig









# 检查电梯执行动作
def check_elevator(elevator):
    # 开门
    if elevator.wait == 0:
        if elevator.tar_floor > elevator.cur_floor:
            elevator.up()
        elif elevator.tar_floor < elevator.cur_floor:
            elevator.down()
        else:
            elevator.arr()
    else:
        elevator.wait += 1
    if elevator.wait == 4:
        elevator.close_door()


# 产生乘客需求
# def make_request(elevators, clock, pro):
#     if np.random.rand() < pro:
#         req_floor = np.random.randint(1, max_floor + 1)
#         arr_floor = np.random.randint(1, max_floor + 1)
#         # 当如果请求到达楼层和当前所在楼层相同时，重新产生乘客请求
#         while arr_floor == req_floor:
#             arr_floor = np.random.randint(1, max_floor + 1)
#         p = Passenger(req_floor, arr_floor, clock)

#         dis_list = [ele.alloc_cost(p.req_floor, p.direction) for ele in elevators]
#         print(dis_list)
#         print('控制台信息：{0}楼用户请求使用{1}号电梯到达{2}楼，ID: {3}'.format(req_floor, dis_list.index(min(dis_list)), arr_floor, p.id))
#         elevators[dis_list.index(min(dis_list))].req_que.append(p)
#         print("-----------------------")
#         print("{}号电梯重新搜索请求".format(dis_list.index(min(dis_list))))
#         elevators[dis_list.index(min(dis_list))].set_target_floor()
#         elevators[dis_list.index(min(dis_list))].set_direction()

def make_request(elevators, clock):
    # 初始化文件读取相关状态
    if not hasattr(make_request, 'request_generator'):
        # make_request.request_generator = read_requests('requests.txt')
        make_request.request_generator = read_requests('request1.txt')
        # make_request.request_generator = read_requests('request2.txt')
        make_request.next_available = 0
        make_request.is_done = False  # 新增完成标志

    # 如果已经完成则直接返回
    if make_request.is_done:
        return

    # 检查时间间隔
    if clock < make_request.next_available:
        return

    try:
        req_floor, arr_floor = next(make_request.request_generator)
        
        p = Passenger(req_floor, arr_floor, clock)
        
        dis_list = [ele.alloc_cost(p.req_floor, p.direction) for ele in elevators]
        selected_idx = dis_list.index(min(dis_list))
        
        print(f'Console information：User on Floor {req_floor} requests to use Elevator No.{selected_idx} to reach Floor {arr_floor}，ID: {p.id}')
        print("-----------------------")
        print(f"Re-search request for Elevator {selected_idx}")
        
        elevators[selected_idx].req_que.append(p)
        elevators[selected_idx].set_target_floor()
        elevators[selected_idx].set_direction()
        
        # make_request.next_available = clock + np.random.uniform(0.5, 1) #密集用户请求
        make_request.next_available = clock + np.random.uniform(1, 3) #稀疏用户请求
        
    except StopIteration:
        make_request.is_done = True  # 标记处理完成
        print("All requests have been processed and completed")
        return  # 关键：立即退出避免后续操作

def read_requests(filename):
    with open(filename, 'r') as f:
        for line in f:
            line = line.strip()
            if not line or line.startswith('#'):
                continue
            try:
                req, arr = map(int, line.split())
                if req == arr:
                    continue
                yield req, arr
            except ValueError:
                continue


# def make_request(elevators, clock, file_path):
#     try:
#         # 打开文件，准备逐行读取
#         with open(file_path, 'r') as file:
#             for line in file:
#                 # 去掉行末的换行符，并按空格分割出 req_floor 和 arr_floor
#                 req_floor, arr_floor = map(int, line.strip().split())
                
#                 # 创建乘客对象
#                 p = Passenger(req_floor, arr_floor, clock)

#                 # 计算每个电梯分配到该请求的成本
#                 dis_list = [ele.alloc_cost(p.req_floor, p.direction) for ele in elevators]
#                 print(dis_list)
#                 print('控制台信息：{0}楼用户请求使用{1}号电梯到达{2}楼，ID: {3}'.format(
#                     req_floor, dis_list.index(min(dis_list)), arr_floor, p.id))

#                 # 将请求分配给成本最小的电梯
#                 elevators[dis_list.index(min(dis_list))].req_que.append(p)
#                 print("-----------------------")
#                 print("{}号电梯重新搜索请求".format(dis_list.index(min(dis_list))))

#                 # 更新电梯的目标楼层和方向
#                 elevators[dis_list.index(min(dis_list))].set_target_floor()
#                 elevators[dis_list.index(min(dis_list))].set_direction()

#                 # 随机等待 1-3 秒
#                 wait_time = np.random.uniform(1, 3)
#                 print(f"等待 {wait_time:.2f} 秒...")
#                 time.sleep(wait_time)

#     except FileNotFoundError:
#         print(f"错误：文件 {file_path} 未找到！")
#     except ValueError:
#         print("错误：文件内容格式不正确，请确保每行包含两个整数（req_floor 和 arr_floor）。")
#     except Exception as e:
#         print(f"发生未知错误：{e}")


def draw_elevator(elevators, ax):
    ax.axis('off')
    # ax.add_patch(plt.Rectangle((0, elevators[0].cur_floor / max_floor - 1.0 / max_floor), 0.1, 0.1, fill=elevators[0].door, edgecolor='blue'))
    # ax.add_patch(plt.Rectangle((0.1, elevators[0].cur_floor / max_floor - 1.0 / max_floor), 0.1, 0.1, fill=elevators[0].door, edgecolor='blue'))

    # ax.add_patch(plt.Rectangle((0.4, elevators[1].cur_floor / max_floor - 1.0 / max_floor), 0.1, 0.1, fill=elevators[1].door, edgecolor='blue'))
    # ax.add_patch(plt.Rectangle((0.5, elevators[1].cur_floor / max_floor - 1.0 / max_floor), 0.1, 0.1, fill=elevators[1].door, edgecolor='blue'))

    # ax.add_patch(plt.Rectangle((0.8, elevators[2].cur_floor / max_floor - 1.0 / max_floor), 0.1, 0.1, fill=elevators[2].door, edgecolor='blue'))
    # ax.add_patch(plt.Rectangle((0.9, elevators[2].cur_floor / max_floor - 1.0 / max_floor), 0.1, 0.1, fill=elevators[2].door, edgecolor='blue'))

    ax.add_patch(plt.Rectangle((0, elevators[0].cur_floor / globalConfig.max_floor - 1.0 / globalConfig.max_floor), 0.1, 0.1, fill=elevators[0].door, edgecolor='blue'))
    ax.add_patch(plt.Rectangle((0.1, elevators[0].cur_floor / globalConfig.max_floor - 1.0 / globalConfig.max_floor), 0.1, 0.1, fill=elevators[0].door, edgecolor='blue'))

    ax.add_patch(plt.Rectangle((0.25, elevators[1].cur_floor / globalConfig.max_floor - 1.0 / globalConfig.max_floor), 0.1, 0.1, fill=elevators[1].door, edgecolor='blue'))
    ax.add_patch(plt.Rectangle((0.35, elevators[1].cur_floor / globalConfig.max_floor - 1.0 / globalConfig.max_floor), 0.1, 0.1, fill=elevators[1].door, edgecolor='blue'))

    ax.add_patch(plt.Rectangle((0.5, elevators[2].cur_floor / globalConfig.max_floor - 1.0 / globalConfig.max_floor), 0.1, 0.1, fill=elevators[2].door, edgecolor='blue'))
    ax.add_patch(plt.Rectangle((0.6, elevators[2].cur_floor / globalConfig.max_floor - 1.0 / globalConfig.max_floor), 0.1, 0.1, fill=elevators[2].door, edgecolor='blue'))

    ax.add_patch(plt.Rectangle((0.75, elevators[3].cur_floor / globalConfig.max_floor - 1.0 / globalConfig.max_floor), 0.1, 0.1, fill=elevators[3].door, edgecolor='blue'))
    ax.add_patch(plt.Rectangle((0.85, elevators[3].cur_floor / globalConfig.max_floor - 1.0 / globalConfig.max_floor), 0.1, 0.1, fill=elevators[3].door, edgecolor='blue'))

    plt.savefig("elevator.png")
    # plt.show()
    plt.cla()


class WorkThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self._stop_event = threading.Event()

    def stop(self):
        self._stop_event.set()

    def run(self):
        # ele = Elevator()
        elevators = [Elevator(), Elevator(), Elevator(), Elevator()]
        
        _fig, ax = plt.subplots(figsize=(5, 5))

        for i in range(1000):

            if self._stop_event.is_set():
                break

            time.sleep(0.4)

            # 统计时间
            globalConfig.time_count += 0.4

            make_request(elevators, globalConfig.time_count)
            # make_request(elevators, time_count, r'C:\Users\李云鹏\Desktop\英方\requests.txt')

            for j, ele in enumerate(elevators):
                print("@@@@@@@@@@@@@@@@@@@@@@")
                print("Elevator No.{}".format(j + 1))
                print("**********************")
                print('Current floor：{}'.format(ele.cur_floor))

                if ele.tar_floor is None:
                    ele.set_target_floor()
                    ele.set_direction()
                    if ele.tar_floor is None:
                        print('The elevator is idle')
                        if ele.wait != 0:
                            ele.wait += 1
                        if ele.wait == 4:
                            ele.close_door()
                    else:
                        check_elevator(ele)
                else:
                    check_elevator(ele)
            draw_elevator(elevators, ax)
            wx.CallAfter(pub.sendMessage, 'change_elevator', elevators=elevators)


class ElevatorFrame(wx.Frame):
    def __init__(self, *args, **kw):
        # ensure the parent's __init__ is called
        super(ElevatorFrame, self).__init__(*args, **kw)

        self.thread = None
        self.floor = 1

        self.max_floor = 10
        self.r_time = 0

        pub.subscribe(self.update_elevator, 'change_elevator')

        _fig, ax = plt.subplots(figsize=(5, 5))
        ax.axis('off')
        # ax.add_patch(plt.Rectangle((0, 0), 0.1, 0.1))
        # ax.add_patch(plt.Rectangle((0.1, 0), 0.1, 0.1))

        # ax.add_patch(plt.Rectangle((0.4, 0), 0.1, 0.1))
        # ax.add_patch(plt.Rectangle((0.5, 0), 0.1, 0.1))

        # ax.add_patch(plt.Rectangle((0.8, 0), 0.1, 0.1))
        # ax.add_patch(plt.Rectangle((0.9, 0), 0.1, 0.1))

        ax.add_patch(plt.Rectangle((0, 0), 0.1, 0.1))
        ax.add_patch(plt.Rectangle((0.1, 0), 0.1, 0.1))

        ax.add_patch(plt.Rectangle((0.25, 0), 0.1, 0.1))
        ax.add_patch(plt.Rectangle((0.35, 0), 0.1, 0.1))

        ax.add_patch(plt.Rectangle((0.5, 0), 0.1, 0.1))
        ax.add_patch(plt.Rectangle((0.6, 0), 0.1, 0.1))

        ax.add_patch(plt.Rectangle((0.75, 0), 0.1, 0.1))
        ax.add_patch(plt.Rectangle((0.85, 0), 0.1, 0.1))

        plt.savefig("elevator.png")
        # plt.show()
        plt.cla()

        # create a panel in the frame
        self.pnl = wx.Panel(self)
        bt_start = wx.Button(self.pnl, label="Start")
        self.Bind(wx.EVT_BUTTON, self.onStartButton, bt_start)
        bt_stop = wx.Button(self.pnl, label="Stop")
        self.Bind(wx.EVT_BUTTON, self.onStopButton, bt_stop)
        buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
        buttonSizer.Add(bt_start)
        buttonSizer.Add(bt_stop)
        bodySizer = wx.BoxSizer(wx.HORIZONTAL)
        image = wx.Image('elevator.png', wx.BITMAP_TYPE_PNG)
        temp = image.ConvertToBitmap()
        self.bmp = wx.StaticBitmap(parent=self.pnl, bitmap=temp)
        self.split_line = "****************************"
        status_label = self.split_line + "\nCurrent floor\n" + self.split_line + "\nPassengers in elevator\n" + self.split_line + "\nPassenger request\n"
        self.time_text = wx.StaticText(self.pnl, label="Average turnover time: None    @    Finished people: 0    @    Average waiting time: None")
        self.status1 = wx.StaticText(self.pnl, label="Elevator No.{}\n".format(1) + status_label)
        self.status2 = wx.StaticText(self.pnl, label="Elevator No.{}\n".format(2) + status_label)
        self.status3 = wx.StaticText(self.pnl, label="Elevator No.{}\n".format(3) + status_label)
        self.status4 = wx.StaticText(self.pnl, label="Elevator No.{}\n".format(4) + status_label)
        self.status_list = [self.status1, self.status2, self.status3, self.status4]
        font = self.status1.GetFont()
        font.PointSize += 2
        self.time_text.SetFont(font)
        self.status1.SetFont(font)
        self.status2.SetFont(font)
        self.status3.SetFont(font)
        self.status4.SetFont(font)
        bodySizer.Add(self.bmp)
        bodySizer.Add(self.status1, 0, wx.LEFT, 10)
        bodySizer.Add(self.status2, 0, wx.LEFT, 10)
        bodySizer.Add(self.status3, 0, wx.LEFT, 10)
        bodySizer.Add(self.status4, 0, wx.LEFT, 10)
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        mainSizer.Add(buttonSizer, 0, wx.ALIGN_CENTER | wx.RIGHT, 100)
        mainSizer.Add(self.time_text, 0, wx.ALIGN_CENTER | wx.RIGHT, 100)
        mainSizer.Add(bodySizer, 0, wx.RIGHT, 100)
        self.pnl.SetSizer(mainSizer)
        self.pnl.Fit()

    def update_elevator(self, elevators):
        # global sum_time
        # global finished_p
        image = wx.Image('elevator.png', wx.BITMAP_TYPE_PNG)
        temp = image.ConvertToBitmap()
        self.bmp.SetBitmap(temp)
        if globalConfig.finished_p > 0:
            self.time_text.SetLabel("Average turnover time: None: {}    @    Finished people: {}    @    Average waiting time: {}".format(round(globalConfig.sum_time/globalConfig.finished_p, 2), globalConfig.finished_p, round(globalConfig.wait_time/globalConfig.finished_p, 2)))
        for i, elevator in enumerate(elevators):
            status_label = self.split_line + "\nCurrent floor\n{}\n".format(round(elevator.cur_floor))
            status_label += self.split_line + "\nPassengers in elevator\n"
            if elevator.carried:
                for p in elevator.carried:
                    status_label += "ID:{}, R_floor:{}, A_floor:{}\n".format(p.id, p.req_floor, p.arr_floor)
            status_label += self.split_line + "\nPassenger request\n"
            if elevator.req_que:
                for p in elevator.req_que:
                    status_label += "ID:{}, R_floor:{}, A_floor:{}\n".format(p.id, p.req_floor, p.arr_floor)
            self.status_list[i].SetLabel("Elevator No.{}\n".format(i + 1) + status_label)

    def onStartButton(self, evt):
        self.thread = WorkThread()
        self.thread.start()

    def onStopButton(self, evt):
        self.thread.stop()

if __name__ == '__main__':
    app = wx.App()
    frm = ElevatorFrame(None, title='Elevator Scheduling Simulation Software')
    frm.Fit()
    frm.Show()
    app.MainLoop()
